
# coding=utf-8

from enum import Enum
from io import StringIO
from typing import List, Tuple

# 定义一个编码类型可能是Enum也可能是Struct
class EncodeType:

    def __init__(self, name: str, desc: str = None, isEnum=False, isStruct=False) -> None:
        if isStruct:
            self.__type_name = name
        else:
            self.__type_name = name[0].upper()+name[1:]
        self.__desc = desc
        if self.__desc is not None:
            with StringIO(self.__desc) as ios:
                self.__desc="\t".expandtabs(1).join([line.strip() for line in ios.readlines()])

        self.__isEnum = isEnum
        self.__isStruct = isStruct
        self.members: List[Tuple[str, str]] = []

    def addMember(self, member: str, desc: str = None):
        if desc is not None:
            with StringIO(desc) as ios:
                desc="\t".expandtabs(1).join([line.strip() for line in ios.readlines()])
        self.members.append((member, desc))

    def getTypeName(self) -> str:
        return self.__type_name

    def getTypeDesc(self) -> str:
        return self.__desc

    def isEnum(self) -> bool:
        return self.__isEnum

    def isStruct(self) -> bool:
        return self.__isStruct

    def toProtpBufferContent(self)->List[str]:
        #不支持枚举编码类型创建protobuf
        contents=[]
        if self.isEnum():
            return contents
        index=0
        def parseTypeWithProto(filedType:str):
            if filedType in ["int8_t","uint8_t","int16_t","uint16_t","int32_t","uint32_t"]:
                return "int32"
            elif filedType in ["int64_t","uint64_t"]:
                return "int64"
            else:
                raise Exception("未知类型{}".format(filedType))
        for (member,desc) in self.members:
            (filedType,filedName)=member.split(" ")
            index+=1
            isArray=filedName.find("[") != -1 and filedName.endswith("]")
            if isArray:
                filedName = filedName[:filedName.find("[")]
            if isArray:
                contents.append("repeated {} {} = {};".format(parseTypeWithProto(filedType),filedName,index))
            else:
                contents.append("required {} {} = {};".format(parseTypeWithProto(filedType),filedName,index))
        return contents

class VhalType(Enum):
    boolean =1
    int32_t =2
    int32_t_array = 3
    int64_t = 4
    int64_t_array = 5
    float_t =6
    float_t_array = 7
    string = 8
    bytes = 9
    
class TypeEnum(Enum):
    int8_t = 1
    uint8_t = 2
    int16_t = 3
    uint16_t = 4
    int32_t = 5
    uint32_t = 6
    int64_t = 7
    uint64_t = 8
    float=9
    string = 10
    int8_t_array=11
    uint8_t_array=12
    int16_t_array = 13
    uint16_t_array = 14
    int32_t_array = 15
    uint32_t_array = 16
    int64_t_array = 17
    uint64_t_array = 18
    float_array = 19
    struct_type =20

    def isArray(self):
        return self.value >= 11 and self.value <=19
    
    def isNormal(self):
        return self.value <=9
    
    def isString(self):
        return self.value == 10
    
    def isStruct(self):
        return self.value == 20


    @classmethod
    def toEnum(cls,type:str):
        if type.strip() == "int8[]":
            return cls.int8_t_array
        if type.strip() == "uint8[]":
            return cls.uint8_t_array
        if type.strip() == "int16[]":
            return cls.int16_t_array
        if type.strip() == "uint16[]":
            return cls.uint16_t_array
        if type.strip() == "int32[]":
            return cls.int32_t_array
        if type.strip() == "uint32[]":
            return cls.uint32_t_array
        if type.strip() == "int64[]":
            return cls.int64_t_array
        if type.strip() == "uint64[]":
            return cls.uint64_t_array
        if type.strip() == "float[]":
            return cls.float_array
        if type.strip() == "string":
            return cls.string
        if type.strip() == "struct":
            return cls.struct_type
        if type.strip() == "float":
            return cls.float
        if type.strip().startswith("int8"):
            return cls.int8_t
        if type.strip().startswith("uint8"):
            return cls.uint8_t
        if type.strip().startswith("int16"):
            return cls.int16_t
        if type.strip().startswith("uint16"):
            return cls.uint16_t
        if type.strip().startswith("int32"):
            return cls.int32_t
        if type.strip().startswith("uint32"):
            return cls.uint32_t
        if type.strip().startswith("int64"):
            return cls.int64_t
        if type.strip().startswith("uint64"):
            return cls.uint64_t
        
        return None

    @classmethod
    def validatePayloadType(cls,type:str)->bool:
        return cls.toEnum(type) is not None

# 定义一个结构提成员,有如下表示方式
# int8_t a
# int8_t a[10]
#
class StructMember:

    def __init__(self, type: TypeEnum, name: str, desc: str = None, encodeType: EncodeType = None) -> None:
        self.member_type = type
        self.is_array = name.find("[") != -1 and name.endswith("]")
        if self.is_array:
            self.array_len = int(name[name.find("[")+1:len(name)-1])
            self.member_name = name[:name.find("[")]
        else:
            self.array_len = 0
            self.member_name = name
        if desc is not None:
            desc = desc.replace('\n', '').replace('\r', '')
        self.desc = desc
        self.__encode_type = encodeType

    def toNet(self,target:str)->str:
        if self.member_type == TypeEnum.int32_t or self.member_type == TypeEnum.uint32_t:
            return "htonl({})".format(target)
        elif self.member_type == TypeEnum.int16_t or self.member_type == TypeEnum.uint16_t:
            return "htons({})".format(target)
        else:
            return target
    
    def fromNet(self,input:str)->str:
        if self.member_type == TypeEnum.int32_t or self.member_type == TypeEnum.uint32_t:
            return "ntohl({})".format(input)
        elif self.member_type == TypeEnum.int16_t or self.member_type == TypeEnum.uint16_t:
            return "ntohs({})".format(input)
        else:
            return input
    
    def hasEncodeType(self)->bool:
        return self.__encode_type is not None
    
    def getEncodeType(self)->EncodeType:
        return self.__encode_type

    def __str__(self) -> str:
        if self.is_array:
            return "{} {}[{}]".format(self.member_type.name,self.member_name,str(self.array_len))
        else:
            return "{} {}".format(self.member_type.name,self.member_name)
    



# 定义一个接收结构体的Payload 不支持结构提嵌套
class StructPayload:

    def __init__(self, type_name: str,name:str, desc: str = None) -> None:
        self.type_name = type_name
        self.name =name
        if desc is not None:
            desc = desc.replace('\n', '').replace('\r', '')
        self.type_desc = desc
        self.members: List[StructMember] = []

    def addMember(self, member:StructMember):
        self.members.append(member)

    def __str__(self) -> str:
        return "{} {}".format(self.type_name,self.name)
    
    def toProtpBufferContent(self)->List[str]:
        #不支持枚举编码类型创建protobuf
        contents=[]
        index=0
        def parseTypeWithProto(filedType:str):
            if filedType in ["int8_t","uint8_t","int16_t","uint16_t","int32_t","uint32_t"]:
                return "int32"
            elif filedType in ["int64_t","uint64_t"]:
                return "int64"
            else:
                raise Exception("未知类型{}".format(filedType))
        for member in self.members:
            filedType=member.member_type.name
            filedName = member.member_name
            index+=1
            if member.is_array:
                contents.append("repeated {} {} = {};".format(parseTypeWithProto(filedType),filedName,index))
            else:
                contents.append("required {} {} = {};".format(parseTypeWithProto(filedType),filedName,index))
        return contents


class ArrayPayload:

    def __init__(self, type_name: TypeEnum, name: str, len: str, desc: str = None) -> None:
        self.array_type = type_name
        self.array_name = name
        self.array_len = len
        if desc is not None:
            desc = desc.replace('\n', '').replace('\r', '')
        self.desc = desc

    def toNet(self,target:str)->str:
        if self.array_type == TypeEnum.int32_t:
            return "htonl({})".format(target)
        elif self.array_type == TypeEnum.int16_t:
            return "htons({})".format(target)
        else:
            return target

    def __str__(self) -> str:
        if self.array_type == TypeEnum.string:
            return "uint8_t {}[{}]".format(self.array_name,self.array_len)
        return "{} {}[{}]".format(self.array_type.name,self.array_name,self.array_len)


class BaseTypePayload:

    def __init__(self,type_name:TypeEnum,name:str,encodeType: EncodeType = None) -> None:
        self.type = type_name
        self.name = name
        self.__encodeType = encodeType
    
    def hasEncodeType(self)->bool:
        return self.__encodeType is not None
    
    def getEncodeType(self)->EncodeType:
        return self.__encodeType

    def toNet(self,target:str)->str:
        if self.type == TypeEnum.int32_t or self.type == TypeEnum.uint32_t:
            return "htonl({})".format(target)
        elif self.type == TypeEnum.int16_t or self.type == TypeEnum.uint16_t:
            return "htons({})".format(target)
        else:
            return target
        
    


    def __str__(self) -> str:
        return "{} {}".format(self.type.name,self.name)


class Payload:

    def __init__(self) -> None:
        self.__struct_type: StructPayload = None
        self.__array_type: ArrayPayload = None
        self.__base_type: BaseTypePayload = None

    def isStructPayload(self) -> bool:
        return self.__struct_type is not None

    def isArrayPayload(self) -> bool:
        return self.__array_type is not None

    def isBaseTypePayload(self) -> bool:
        return self.__base_type is not None

    def getStructPayload(self) -> StructPayload:
        return self.__struct_type

    def getArrayPayload(self) -> ArrayPayload:
        return self.__array_type

    def getBaseTypePayload(self) -> BaseTypePayload:
        return self.__base_type

    def setStructPayload(self, payload: StructPayload):
        self.__struct_type = payload

    def setArrayPayload(self, payload: ArrayPayload):
        self.__array_type = payload

    def setBaseTypePayload(self, payload: BaseTypePayload):
        self.__base_type = payload


# 定义一个信号的值相关描述
# class SignalValue:

#     def __init__(self, value_name, sizeOfSignal: str, encode_type: EncodeType = None, factor="1", offset="0") -> None:
#         self.__valueName = value_name
#         self.__valueType:TypeEnum = self.__parseValueType(sizeOfSignal)
#         self.__encodeType: EncodeType = encode_type
#         self.__factor = factor
#         self.__offset = offset

#     @classmethod
#     def initByDict(cls, signalItemDict, encodeTypeDict: Dict[str, EncodeType], isTimeout=False):
#         # 确定值的名字
#         if isTX(signalItemDict):
#             if signalItemDict.get("SignalGroup", None) is None:
#                 # 如果没有SignalGroup,则表示这是一个单一的信号,名字固定value
#                 valueName = "value"
#             else:
#                 # 这是一个信号组,信号值的名字就是信号的名字
#                 signalName = signalItemDict["Signal"]
#                 valueName = signalName[:1].lower()+signalName[1:]
#         else:
#             # 如果是rx信号则rx信号是一包出来的所以名字就是信号名字
#             signalName = signalItemDict["Signal"]
#             valueName = signalName[:1].lower()+signalName[1:]
#         if isTimeout:
#             encodingTypeName = "Timeout"
#         else:
#             encodingTypeName = signalItemDict.get("EncodingType", None)
#         encodeType = None
#         if encodingTypeName is not None:
#             encodeType = encodeTypeDict.get(encodingTypeName, None)
#         factor = str(signalItemDict.get("Factor", "1"))
#         offset = str(signalItemDict.get("Offset", "0"))
#         sizeofSignal = signalItemDict["SizeofSignal"]
#         return cls(valueName, sizeofSignal, encodeType, factor, offset)

#     def __parseValueType(self, size):
#         len = int(size, 10)
#         if((len >= 1) and (len <= 8)):
#             return TypeEnum.uint8_t
#         elif((len > 8) and (len <= 16)):
#             return TypeEnum.uint16_t
#         else:
#             return TypeEnum.uint32_t

#     # 检查该信号的值是否有枚举定义
#     def hasEnum(self) -> bool:
#         return self.__encodeType is not None

#     # 如果这个信号有枚举值定义,则该函数返回枚举名称
#     def getEncodeType(self) -> EncodeType:
#         return self.__encodeType

#     def setEncodeType(self, encodeType: EncodeType):
#         self.__encodeType = encodeType

#     # 获取值的名称
#     def getValueName(self) -> str:
#         return self.__valueName

#     # 获取值定义的类型
#     def getValueType(self) -> TypeEnum:
#         return self.__valueType

#     # 用来检查该信号使用的时候的类型
#     def isFloat(self) -> bool:
#         return isinstance(eval(self.__factor), float) or isinstance(eval(self.__offset), float)

#     def __getNtohx(self, member) -> str:
#         if self.__valueType == TypeEnum.uint16_t:
#             return "ntohs({0})".format(member)
#         elif self.__valueType == TypeEnum.uint32_t:
#             return "(int32_t)ntohl({0})".format(member)
#         else:
#             return member

#     def __getHtonx(self) -> str:
#         if self.__valueType == TypeEnum.uint16_t:
#             return "htons"
#         elif self.__valueType == TypeEnum.uint32_t:
#             return "htonl"
#         else:
#             return ""

#     def readSignalFromNet(self, member: str) -> str:
#         fromNet = self.__getNtohx(member)
#         factor_unit = "f" if isinstance(eval(self.__factor), float) else ""
#         offset_unit = "f" if isinstance(eval(self.__offset), float) else ""
#         if self.__factor != "1" and self.__offset != "0":
#             return "{0}*({1}{2})+({3}{4})".format(fromNet, self.__factor, factor_unit, self.__offset, offset_unit)
#         elif self.__factor != "1":
#             return "{0}*({1}{2})".format(fromNet, self.__factor, factor_unit)
#         elif self.__offset != "0":
#             return "{0}+({1}{2})".format(fromNet, self.__offset, offset_unit)
#         else:
#             return fromNet

#     # htonl((uint8_t)signal.value.int32Value[0])
#     # htons(signal.value.int32Value[0])
#     def setSignalToNet(self, member: str) -> str:
#         toNet = self.__getHtonx()
#         if self.__factor != "1" and self.__offset != "0":
#             if toNet != "":
#                 return "{0}(({1})(({2}-{3})/{4}))".format(toNet, self.__valueType.name, member, "({0})".format(self.__offset) if str(self.__offset).startswith("-") else self.__offset, self.__factor)
#             else:
#                 return "({0})(({1}-{2})/{3})".format(self.__valueType.name, member, "({0})".format(self.__offset) if str(self.__offset).startswith("-") else self.__offset, self.__factor)
#         elif self.__factor != "1":
#             if toNet != "":
#                 return "{0}(({1})({2}/{3}))".format(toNet, self.__valueType.name, member, self.__factor)
#             else:
#                 return "({0})({1}/{2})".format(self.__valueType.name, member, self.__factor)
#         elif self.__offset != "0":
#             if toNet != "":
#                 return "{0}(({1})({2}-{3}))".format(toNet, self.__valueType.name, member, "({0})".format(self.__offset) if str(self.__offset).startswith("-") else self.__offset)
#             else:
#                 return "({0})({1}-{2})".format(self.__valueType.name, member, "({0})".format(self.__offset) if str(self.__offset).startswith("-") else self.__offset)
#         else:
#             if toNet != "":
#                 return "{0}(({1}){2})".format(toNet,self.__valueType.name ,member)
#             else:
#                 return "({0}){1}".format(self.__valueType.name,member)
